/**
 *  list.c - Routines for manipulating lists.
 *
 *  Copyright (C) 2008-2009 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "include/list.h"


static void __add_node(list_t *next, list_t *prev, list_t *pnew) {
    if((next == 0) || (prev == 0) || (pnew == 0)) {
        return;
    }

    next->prev = pnew;
    pnew->next = next;
    pnew->prev = prev;
    prev->next = pnew;
}



static void add_que_rear(list_t *rear, list_t *pnew) {
    if((rear == 0) || (pnew == 0)) {
        return;
    }

    __add_node(rear->next, rear, pnew);
}



static void del_que(list_t *next, list_t *prev) {
    if((next == 0) || (prev == 0)) {
        return;
    }

    next->prev = prev;
    prev->next = next;
}



/**
 * list_node_init - Initailize a node.
 * @node: Pointer to the node.
 * @return:
 *
 * @notes:
 */
void list_node_init(list_t *node) {
    if(node == 0) {
        return;
    }

    node->next = node;
    node->prev = node;
}



/**
 * add_node_list_rear - Add a new node to rear of a Double-linked list.
 * @head: The head of Double-linked list.
 * @pnew: Pointer to the new node.
 * @return:
 *
 * @notes: the head have to initialize with list_node_init.
 *
 */
void add_node_list_rear(list_t *head, list_t *pnew) {
    if((head == 0) || (pnew == 0)) {
        return;
    }

    if((head->next == head) && (head->prev == head)) {
        head->next = pnew;
        head->prev = pnew;
        pnew->next = pnew;
        pnew->prev = pnew;
    } else {
        add_que_rear(head->prev, pnew);
        head->prev = pnew;
    }
}



/**
 * del_node_list - Delete a node from a Double-linked list.
 * @head: The head of Double-linked list.
 * @pdel: Pointer to the deleted node.
 * @return: The deleted node.
 *
 * @notes: The list must be a valide double-linked list, that is
 * the head was initialized with list_node_init, pdel was in list.
 *
 */
list_t *del_node_list(list_t *head, list_t *pdel) {
    list_t *renode = ((void*)0);

    if((head == (void*)0) || (pdel == (void*)0) || (head == pdel)) {
        return(renode);
    }

    renode = pdel;

    /* node is exist in the list */
    if((head->next != head) && (head->prev != head)) {
        /* there is only one node in queue currently */
        if((head->next == pdel) && (head->prev == pdel)) {
            head->next = head;
            head->prev = head;
        }

        /* node is in the head of queue */
        else if((head->next == pdel) && (head->prev != pdel)) {
            head->next = pdel->next;
            del_que(pdel->next, pdel->prev);
        }

        /* node is in the rear of queue */
        else if((head->next != pdel) && (head->prev == pdel)) {
            head->prev = pdel->prev;
            del_que(pdel->next, pdel->prev);
        } else {
            del_que(pdel->next, pdel->prev);
        }
        list_node_init(pdel);
        return(renode);
    } else {
        return(0);
    }
}



/* sigle list */

/**
 * list_node_single_init - Initailize a node.
 * @node: Pointer to the node.
 * @return:
 *
 * @notes:
 */
void list_node_single_init(slist_t *node) {
    if(node != ((void*)0)) {
        node->next = 0;
    }
}



/**
 * add_node_single_list_head - Add a new node to rear of a single list.
 * @head: The head of single list.
 * @pnew: Pointer to the new node.
 * @return:
 *
 * @notes:
 */
void add_node_single_list_head(slist_t *head, slist_t *pnew) {
    if((head == 0) || (pnew == 0) || (head == pnew)) {
        return;
    }

    pnew->next = head->next;
    head->next = pnew;
}



/**
 * del_node_single_list - Delete a node from a single list.
 * @head: The head of single list.
 * @pdel: Pointer to the deleted node.
 * @return: The deleted node.
 *
 * @notes:
 */
slist_t *del_node_single_list(slist_t *head, slist_t *pdel) {
    slist_t *plist = ((void*)0);

    if((head == 0) || (head->next == 0) || (pdel == 0) || (head == pdel)) {
        return(0);
    }

    plist = head;
    while((plist != 0) && (plist->next != 0) && (plist->next != pdel)) {
        plist = plist->next;
    }

    if(plist->next != 0) {  /* pdel is in list */
        plist->next = pdel->next;
        pdel->next = 0;
    }
    return(pdel);
}

